import json
import re

from django.contrib.auth import login, authenticate, logout
from django.contrib.auth.mixins import LoginRequiredMixin
from django.core.cache import cache
from django.http import HttpResponse
from django.http import HttpResponseForbidden
from django.http import HttpResponseServerError
from django.http import JsonResponse
from django.shortcuts import render, redirect

from apps.area.models import Area
from celery_tasks.email.tasks import send_verify_email
# Create your views here.
from django.urls import reverse
from django.utils import http
from django.views import View
from django_redis import get_redis_connection
from pymysql import DatabaseError

from apps.users.models import User, Address
from apps.goods.models import SKU
from meiduo_mall.settings.dev import logger
from apps.users.utils import LoginRequiredJSONMixin, generate_verify_email_url, check_verify_email_url

from utils.response_code import RETCODE


class RegisterView(View):
    '''用户注册'''

    def get(self, request):
        return render(request, 'register.html')

    def post(self, request):

        # 1.接受参数
        username = request.POST.get('user_name')
        password = request.POST.get('pwd')
        password2 = request.POST.get('cpwd')
        mobile = request.POST.get('phone')
        allow = request.POST.get('allow')
        # 2.校验参数
        # 判断参数是否齐全
        if not all([username, password, password2, mobile, allow]):
            return HttpResponseForbidden('缺少必传参数')
        # 判断用户名是否是5-20个字符
        if not re.match(r'^[a-zA-Z0-9_-]{5,20}$', username):
            return HttpResponseForbidden('请输入5-20个字符的用户名')
        # 判断密码是否是8-20个数字
        if not re.match(r'^[0-9A-Za-z]{8,20}$', password):
            return HttpResponseForbidden('请输入8-20位的密码')
        # 判断两次密码是否一致
        if password != password2:
            return HttpResponseForbidden('两次输入的密码不一致')
        # 判断手机号是否合法
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return HttpResponseForbidden('请输入正确的手机号码')
        # 判断是否勾选用户协议
        if allow != 'on':
            return HttpResponseForbidden('请勾选用户协议')

        # 判断用户短信验正吗是否正确
        sms_code = request.POST.get('msg_code')
        redis_code_cli = get_redis_connection('sms_code')
        redis_code = redis_code_cli.get('sms_%s' % mobile).decode()

        if sms_code is None:
            return render(request, 'register.html', {'sms_code_errmsg': '无效的短信验证码'})
        if sms_code != redis_code:
            return render(request, 'register.html', {'sms_code_errmsg': '无效的短信验证码'})

        # 3.处理请求
        try:
            user = User.objects.create_user(username=username, password=password, mobile=mobile)
            # 状态保持
            login(request, user)
        except DatabaseError:
            return render(request, 'register.html', {'register_errmsg': '注册失败'})
        response = redirect(reverse('content:index'))
        response.set_cookie('username', username, max_age=3600 * 24 * 15)
        return response


class UsernameCountView(View):
    '''检验用户名是否重复'''

    def get(self, request, username):
        count = User.objects.filter(username=username).count()
        return JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK', 'count': count})


class MobileCountView(View):
    def get(self, request, mobile):
        count = User.objects.filter(mobile=mobile).count()
        return JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK', 'count': count})


class LoginView(View):
    def get(self, request):
        return render(request, 'login.html')

    def post(self, request):
        # 获取参数
        username = request.POST.get('username')
        password = request.POST.get('pwd')
        remembered = request.POST.get('remembered')
        # 校验参数
        if not all([username, password]):
            return HttpResponseForbidden('参数不齐全')
            # 2.1 用户名
        if not re.match(r'^[a-zA-Z0-9_-]{5,20}$', username):
            return HttpResponseForbidden('请输入5-20个字符的用户名')
            # 2.2 密码
        if not re.match(r'^[0-9A-Za-z]{8,20}$', password):
            return HttpResponseForbidden('请输入8-20位的密码')

        # python自带的user认证
        user = authenticate(username=username, password=password)

        if user is None:
            return render(request, 'login.html', {'account_errmsg': '用户名或密码错误'})

        # 保持登陆
        login(request, user)

        # 是否记住用户名
        if remembered != 'on':
            request.session.set_expiry(0)

        else:
            request.session.set_expiry(None)
        response = redirect(reverse('content:index'))
        response.set_cookie('username', username, max_age=3600 * 24 * 15)

        return response


class LogoutView(View):
    def get(self, request):
        logout(request)
        # 清空cookie
        response = redirect(reverse('users:login'))
        response.delete_cookie('username')
        return response


class UserInfoView(LoginRequiredMixin, View):
    def get(self, request):  # 请求对象里边包含了用户对象,LoginRequiredMixin已经对用户是否登陆进行过了判断

        '''提供个人信息界面'''
        context = {
            'username': request.user.username,
            'mobile': request.user.mobile,
            'email': request.user.email,
            'email_active': request.user.email_active
        }

        return render(request, 'user_center_info.html', context=context)


class EmialView(LoginRequiredJSONMixin, View):

    def put(self, request):  # 前段请求的是put方法
        json_str = request.body.decode()
        json_dict = json.loads(json_str)
        email = json_dict.get('email')

        # 校验参数
        if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
            return HttpResponseForbidden('参数email有误')
        # 对email赋值
        try:
            request.user.email = email
            request.user.save()
        except Exception as e:
            logger.error(e)
            return JsonResponse({'code': RETCODE.DBERR, 'errmsg': '添加邮箱失败'})
        # 异步发送邮件任务
        verify_url = generate_verify_email_url(request.user)

        send_verify_email.delay(email, verify_url)

        # 响应添加邮箱结果
        return JsonResponse({'code': RETCODE.OK, 'errmsg': '添加邮箱成功'})


class VerifyEmailView(View):
    '''验证邮箱'''

    def get(self, request):
        '''实现邮箱验证逻辑'''
        token = request.GET.get('token')
        # 校验参数
        if not token:
            return HttpResponseForbidden('缺少token')

        user = check_verify_email_url(token)
        if not user:
            return HttpResponseForbidden('无效的token')

        try:
            user.email_active = True
            user.save()
        except Exception as e:
            logger.error(e)
            return HttpResponseServerError('邮件激活失败')

        return redirect(reverse('users:info'))


class AddressView(View):
    '''用户收货地址'''

    def get(self, request):
        '''提供用户收货界面'''
        user = request.user
        addresses = Address.objects.filter(user=user, is_deleted=False)
        address_list = []
        for address in addresses:
            address_list.append({
                "id": address.id,
                "title": address.title,
                "receiver": address.receiver,
                "province": address.province.name,
                "city": address.city.name,
                "district": address.district.name,
                "place": address.place,
                "mobile": address.mobile,
                "tel": address.tel,
                "email": address.email
            })
        context = {
            'default_address_id': user.default_address_id,
            'addresses': address_list,
        }

        return render(request, 'user_center_site.html', context)


class AreasView(View):
    '''省市区数据'''

    def get(self, request):
        '''提供收货地址界面'''
        area_id = request.GET.get('area_id')
        if not area_id:
            # 读取省份缓存数据
            province_list = cache.get('province_list')
            if not province_list:
                # 提供省份数据
                try:
                    # 查询省份数据
                    province_midel_list = Area.objects.filter(parent__isnull=True)
                    # 序列化升级数据
                    province_list = []
                    for province_model in province_midel_list:
                        province_list.append({'id': province_model.id, 'name': province_model.name})

                except Exception as e:
                    logger.error(e)
                    return JsonResponse({'code': RETCODE.DBERR, 'errmsg': '省份数据错误'})
                # 响应省份数据
                # 存储省份缓存数据
                cache.set('province_list', province_list, 3600)
            return JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK', 'province_list': province_list})
        else:
            # 读取市或区缓存数据
            sub_data = cache.get('sub_area_' + area_id)

            if not sub_data:
                # 提供市或区数据
                try:
                    parent_model = Area.objects.get(id=area_id)
                    sub_model_list = parent_model.subs.all()
                    sub_list = []
                    for sub_model in sub_model_list:
                        sub_list.append({'id': sub_model.id, 'name': sub_model.name})

                    sub_data = {
                        'id': parent_model.id,
                        'name': parent_model.name,
                        'subs': sub_list
                    }
                except Exception as e:
                    logger.error(e)
                    return JsonResponse({'code': RETCODE.DBERR, 'errmsg': '城市区数据错误'})
                cache.set('sub_area_' + area_id, sub_data, 3600)
            return JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK', 'sub_data': sub_data})


class CreateAddressView(View):
    '''用户新增地址'''

    def post(self, request):
        '''实现新增地址逻辑'''
        # 判断地址收否超过20个
        count = request.user.addresses.count()
        if count >= 20:
            return JsonResponse({'code': RETCODE.THROTTLINGERR, 'errmsg': '超过地址数量上限'})
        # 1.接受数据
        json_dict = json.loads(request.body.decode())
        receiver = json_dict.get('receiver')
        province_id = json_dict.get('province_id')
        city_id = json_dict.get('city_id')
        district_id = json_dict.get('district_id')
        place = json_dict.get('place')
        mobile = json_dict.get('mobile')
        tel = json_dict.get('tel')
        email = json_dict.get('email')
        # 2.进行校验
        if not all([receiver, province_id, city_id, district_id, place, mobile]):
            return HttpResponseForbidden('缺少必传参数')
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return HttpResponseForbidden('参数mobile有误')
        if tel:
            if not re.match(r'^(0[0-9]{2,3}-)?([2-9][0-9]{6,7})+(-[0-9]{1,4})?$', tel):
                return HttpResponseForbidden('参数tel有误')
        if email:
            if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
                return HttpResponseForbidden('参数email有误')
        # 3.处理请求
        try:
            address = Address.objects.create(
                user=request.user,
                title=receiver,
                receiver=receiver,
                province_id=province_id,
                city_id=city_id,
                district_id=district_id,
                place=place,
                mobile=mobile,
                tel=tel,
                email=email
            )
            # 设置默认地址
            if not request.user.default_address:
                request.user.default_address = address
                request.user.save()
        except Exception as e:
            logger.error(e)
            return JsonResponse({'code': RETCODE.DBERR, 'errmsg': '新增地址失败'})
        # 4.返回数据
        address_dict = {  # 返回地址i信息实现局部刷新
            "id": address.id,
            "title": address.title,
            "receiver": address.receiver,
            "province": address.province.name,
            "city": address.city.name,
            "district": address.district.name,
            "place": address.place,
            "mobile": address.mobile,
            "tel": address.tel,
            "email": address.email
        }
        return JsonResponse({'code': RETCODE.OK, 'errmsg': '新增地址成功', 'address': address_dict})


class UpdateDestroyAddressView(View):
    '''修改和删除地址'''

    def put(self, request, address_id):
        '''修改地址i信息'''
        # 1.接受数据
        json_dict = json.loads(request.body.decode())
        receiver = json_dict.get('receiver')
        province_id = json_dict.get('province_id')
        city_id = json_dict.get('city_id')
        district_id = json_dict.get('district_id')
        place = json_dict.get('place')
        mobile = json_dict.get('mobile')
        tel = json_dict.get('tel')
        email = json_dict.get('email')
        # 2.进行校验
        if not all([receiver, province_id, city_id, district_id, place, mobile]):
            return HttpResponseForbidden('缺少必传参数')
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return HttpResponseForbidden('参数mobile有误')
        if tel:
            if not re.match(r'^(0[0-9]{2,3}-)?([2-9][0-9]{6,7})+(-[0-9]{1,4})?$', tel):
                return HttpResponseForbidden('参数tel有误')
        if email:
            if not re.match(r'^[a-z0-9][\w\.\-]*@[a-z0-9\-]+(\.[a-z]{2,5}){1,2}$', email):
                return HttpResponseForbidden('参数email有误')

        try:
            # 判断地址ishi否存在,病更新地址信息
            Address.objects.filter(id=address_id).update(
                user=request.user,
                title=receiver,
                receiver=receiver,
                province_id=province_id,
                city_id=city_id,
                district_id=district_id,
                place=place,
                mobile=mobile,
                tel=tel,
                email=email
            )
        except Exception as e:
            logger.error(e)
            return JsonResponse({'code': RETCODE.DBERR, 'errmsg': '更新地址失败'})
        address = Address.objects.get(id=address_id)
        # 返回请求数据
        address_dict = {
            "id": address.id,
            "title": address.title,
            "receiver": address.receiver,
            "province": address.province.name,
            "city": address.city.name,
            "district": address.district.name,
            "place": address.place,
            "mobile": address.mobile,
            "tel": address.tel,
            "email": address.email
        }
        return JsonResponse({'code': RETCODE.OK, 'errmsg': '更新地址成功', 'address': address_dict})

    def delete(self, request, address_id):
        '''删除地址信息 '''
        try:
            address = Address.objects.get(id=address_id)
            # 将地址逻辑删除
            address.is_deleted = True
            address.save()
        except Exception as e:
            logger.error(e)
            return JsonResponse({'code': RETCODE.DBERR, 'errmsg': '删除地址失败'})
        return JsonResponse({'code': RETCODE.OK, 'errmsg': '删除地址成功'})


class DefaultAddressView(View):
    '''设置默认地址'''

    def put(self, request, address_id):
        try:
            address = Address.objects.get(id=address_id)
            # 设置默认地址
            request.user.default_address = address
            request.user.save()
        except Exception as e:
            logger.error(e)
            return JsonResponse({'code': RETCODE.DBERR, 'errmsg': '设置默认地址失败'})
        return JsonResponse({'code': RETCODE.OK, 'errmsg': '设置默认地址成功'})


class UpdateTitleAddressView(View):
    '''修改地址标题'''

    def put(self, request, address_id):
        '''修改地址标题'''
        # 接受标题参数
        json_dict = json.loads(request.body.decode())
        title = json_dict.get('title')
        try:
            address = Address.objects.get(id=address_id)
            # 将地址逻辑删除
            address.title = title
            address.save()
        except Exception as e:
            logger.error(e)
            return JsonResponse({'code': RETCODE.DBERR, 'errmsg': '设置地址标题失败'})
        return JsonResponse({'code': RETCODE.OK, 'errmsg': '设置地址标题成功'})


class ChangePasswordView(View):
    def get(self, request):
        '''获取请求页面'''
        return render(request, 'user_center_pass.html')

    '''修改密码'''

    def post(self, request):
        '''实现修改密码逻辑'''
        # 接受参数
        old_password = request.POST.get('old_pwd')
        new_password = request.POST.get('new_pwd')
        new_password2 = request.POST.get('new_cpwd')
        # 校验参数
        if not all([old_password, new_password, new_password2]):
            return HttpResponseForbidden('缺少必传参数')
        try:
            request.user.check_password(old_password)
        except Exception as e:
            logger.error(e)
            return render(request, 'user_center_pass.html', {'origin_pwd_errmsg': '原始密码错误'})
        if not re.match(r'^[0-9A-Za-z]{8,20}$', new_password):
            return HttpResponseForbidden('密码最少8位，最长20位')
        if new_password != new_password2:
            return HttpResponseForbidden('两次输入的密码不一致')
        # 处理请求
        try:
            request.user.set_password(new_password)
            request.user.save()
        except Exception as e:
            logger.error(e)
            return render(request, 'user_center_pass.html', {'change_pwd_errmsg': '修改密码失败'})
        logout(request)
        response = redirect(reverse('users:login'))
        response.delete_cookie('username')
        # 返回结果
        return response


class UserBrowseHistory(LoginRequiredMixin, View):
    '''存储用户的历史浏览记录'''

    def get(self, request):
        '''获取用户浏览历史记录'''
        # 1.创建redis连接对象
        redis_client = get_redis_connection('history')
        # 2.获取用户的浏览历史记录的sku_id
        sku_ids = redis_client.lrange('history_{}'.format(request.user.id), 0, -1)
        # 3.遍历sku_id查询数据库
        skus = []
        for sku_id in sku_ids:
            # 4.构建用户返回的sku数据
            '''
            "skus":[
            {
                "id":6,
                "name":"Apple iPhone 8 Plus (A1864) 256GB 深空灰色 移动联通电信4G手机",
                "default_image_url":"http://image.meiduo.site:8888/group1/M00/00/02/CtM3BVrRbI2ARekNAAFZsBqChgk3141998",
                "price":"7988.00"
            },
            ...
            ]
            '''
            sku = SKU.objects.get(id=sku_id)

            skus.append({
                "id": sku.id,
                "name": sku.name,
                "default_image_url": sku.default_image.url,
                "price": sku.price
            })

        # 5.返回数据
        return JsonResponse({"code": "0", "errmsg": "OK", "skus": skus})

    def post(self, request):
        '''存储用户历史记录'''
        # 1.接受参数
        sku_id = json.loads(request.body.decode()).get('sku_id')
        # 2.查询的数据库
        try:
            sku = SKU.objects.get(id=sku_id)
        except Exception as e:
            logger.error(e)
            return HttpResponseForbidden('商品不存在!')
        # 3.存入redis中
        # 3.1建立redis连接对象
        redis_client = get_redis_connection('history')
        history_key = 'history_{}'.format(request.user.id)

        p = redis_client.pipeline()
        # 3.2去重
        p.lrem(history_key, 0, sku_id)
        # 3.3插入
        p.lpush(history_key, sku_id)
        # 3.4返回前五个
        p.ltrim(history_key, 0, 4)

        p.execute()
        return JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK'})
